package com.liar.testwallet;

import static org.web3j.crypto.Hash.sha256;
import static org.web3j.crypto.WalletUtils.generateWalletFile;

import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.google.common.collect.ImmutableList;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicHierarchy;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.UnreadableWalletException;
import org.json.JSONException;
import org.json.JSONObject;
import org.spongycastle.util.encoders.Hex;
import org.web3j.crypto.Bip39Wallet;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.MnemonicUtils;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.Sign;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 以太坊钱包主页
 */
public class EthWalletActivity extends AppCompatActivity {

    private static final String TAG = "EthWalletActivity";

    private static final String password="";

   // String mnemonic = "shine creek tragic unable admit disease oil result various gun kingdom shop";

   //有效的，地址是：0x4C78C36e376EB1D701B415EAFfC15c29c2170981
//    地址：  0x4c78c36e376eb1d701b415eaffc15c29c2170981
//
//    私钥： 0x47ae411da5eb66894aa0dae8ca888128205cc7cb23b38675904fda5545c6b2d5
//
//    公钥：  bed6ad054f6bacfa1a042a33bb89b51bdfedd883d24558cc3563ebf0cb25c
    String mnemonic="length shaft sponsor ankle during bullet coil sketch connect another stock digital";//有效


   //String mnemonic = "balance gold chief rare review cage bike find best brown define collect";
    //以太坊钱包文件
    private WalletFile mWalletFile;
    //当前网络名称
    private TextView mNetworkTitleText;
    //当前钱包地址
    private EditText mWalletAddressText;
    //当前钱包余额
    private TextView mWalletBalanceText;
    //目标钱包地址
    private EditText mToAddressEdit;
    //目标钱包余额
    private TextView mToAddressBalanceText;
    //发送数额
    private EditText mAmountEdit;
    //钱包信息
    private EditText mKeyStoreEdit;
    //钱包私钥
    private EditText mPrivateKeyEdit;

    //创建Web3j实例
    private Web3j mWeb3j = Web3j.build(new HttpService(Constants.ETHEREUM_GOERLI_URL));
    //当前钱包地址
    private String mAddress;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ethereum_wallet);
        initUi();

        testKeyStore();
        //initEthWalletData();
      //  logD("助记词："+generateMnemonic().toString());
//        yao();
//        yao2();
        try {
            createWallet();
        } catch (MnemonicException.MnemonicLengthException e) {
            Log.e("logcat",e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private void testKeyStore() {
        final  String ALIAS_KEY_STORE="my_test_key_store";
         String needEncryptWord="nihaoa";
        EncryptSafeUtil mEncryptSafeUtil= EncryptSafeUtil.getInstance();
        mEncryptSafeUtil.initKeyStore(ALIAS_KEY_STORE);
       String new1= mEncryptSafeUtil.encryptString(needEncryptWord,ALIAS_KEY_STORE);
       String new2=mEncryptSafeUtil.decryptString(new1,ALIAS_KEY_STORE);
       logD("new1="+new1+";new2="+new2);

    }

    private  void logD(String string){
        Log.d(EthWalletController.TAG,string);

    }

    //生成签名 json先传“123456”
    public  String sign(String json, ECKeyPair keyPair) {
        Sign.SignatureData signatureData = Sign.signMessage(json.getBytes(), keyPair);
        JSONObject signatureDataJson = new JSONObject();
        try {
            signatureDataJson.put("v", Hex.toHexString(signatureData.getV()));
            signatureDataJson.put("r", Hex.toHexString(signatureData.getR()));
            signatureDataJson.put("s", Hex.toHexString(signatureData.getS()));
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        return signatureDataJson.toString();
    }


    private void createWords(){
        File fileDir = new File(Environment.getExternalStorageDirectory().getPath() + "/MyWallet");
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

//        StringBuilder sb = new StringBuilder();
//        byte[] entropy = new byte[Words.TWELVE.byteLength()];
//        new SecureRandom().nextBytes(entropy);
//        new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append);
//        String mnemonics = sb.toString();
//        android.util.Log.e("+++","生成的助记词："+mnemonics);
    }

    public  List<String> generateMnemonic() {
        SecureRandom secureRandom = new SecureRandom();
        DeterministicSeed ds = new DeterministicSeed(secureRandom, 128, "", System.currentTimeMillis() / 1000);
        return ds.getMnemonicCode();
    }




    private void privateKey2Address(String privateKeyStr){

        BigInteger privateKey = new BigInteger(privateKeyStr, 16);

// 创建 EC Key Pair
        ECKeyPair ecKeyPair = ECKeyPair.create(privateKey);

// 获取公钥
        byte[] publicKey = ecKeyPair.getPublicKey().toByteArray();
        // 使用 Keys 工具类从公钥计算地址
        //通过公钥生成钱包地址
        String address = Keys.getAddress( ecKeyPair.getPublicKey());

        logD("Public Key (hex): " + Numeric.toHexStringNoPrefix(publicKey));
        logD("Ethereum Address: " + address);


    }



//https://blog.csdn.net/zgsdzczh/article/details/128159789
      void createWallet()  throws MnemonicException.MnemonicLengthException {


         // List<String> str =generateMnemonic();//好用，生成12个有效的助记词



          List<String>  str= new ArrayList<>() ;
          //12位助记词
          str.add("length");
          str.add("shaft");
          str.add("sponsor");
          str.add("ankle");
          str.add("during");
          str.add("bullet");
          str.add("coil");
          str.add("sketch");
          str.add("connect");
          str.add("another");
          str.add("stock");
          str.add("digital");


        //使用助记词生成钱包种子
        byte[] seed = MnemonicCode.toSeed(str, "");
        DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed);
        DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(masterPrivateKey);
        DeterministicKey deterministicKey = deterministicHierarchy
                .deriveChild(BIP44_ETH_ACCOUNT_ZERO_PATH, false, true, new ChildNumber(0));
        byte[] bytes = deterministicKey.getPrivKeyBytes();
        ECKeyPair keyPair = ECKeyPair.create(bytes);


        String privateKey=keyPair.getPrivateKey().toString(16);
        String publicKey=keyPair.getPublicKey().toString(16);
          //通过公钥生成钱包地址
          String address = Keys.getAddress(keyPair.getPublicKey());

        logD("助记词："+str.toString());
        logD("地址："+"0x"+address);
        logD("私钥："+"0x"+privateKey);
        logD("公钥："+keyPair.getPublicKey().toString(16));
          privateKey2Address(privateKey);

        logD("签名:"+sign("12345678",new ECKeyPair(Numeric.toBigInt(privateKey), Numeric.toBigInt(publicKey))));

    }

//    private String privateToMnemonic(String privateKeyHex){
//
//        // 将十六进制的私钥转换为ECKey
//        ECKey ecKey = ECKey.fromPrivate(new BigInteger(privateKeyHex, 16));
//
//        // 创建一个DeterministicKey
//        DeterministicKey deterministicKey = HDKeyDerivation.createMasterPrivateKey(NetworkParameters.prodNet(), ecKey);
//
//        // 使用BIP39生成助记词
//        String mnemonic = BIP39.mnemonicFromEntropy(deterministicKey.getPrivateKeyBytes());
//        return mnemonic;
//
//    }



   @RequiresApi(api = Build.VERSION_CODES.M)
   void   putKeyStore() throws KeyStoreException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException, IOException {
       // 初始化KeyPairGenerator
       KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
       keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(
               "alias", // 别名
               KeyProperties.PURPOSE_SIGN) // 目的，这里用于签名
               .setDigests(KeyProperties.DIGEST_SHA256) // 指定哈希算法
               .build());

      String password="fdfd";

// 生成密钥对
       KeyPair keyPair = keyPairGenerator.generateKeyPair();
       KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
       keyStore.load(null, password.toCharArray()); // password 是用户设置的密码
// 将密钥对存入KeyStore
       keyStore.setEntry("alias", new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), null), null);

    // 保存KeyStore到文件（实际操作中不推荐，更安全的做法是保持在Android Keystore系统中）
    // FileOutputStream fos = new FileOutputStream("/path/to/keystore.jks");
    // keyStore.store(fos, "password".toCharArray());
    // fos.close();

     }

    /**
     * path路径
     */
    private final static ImmutableList<ChildNumber> BIP44_ETH_ACCOUNT_ZERO_PATH =
            ImmutableList.of(new ChildNumber(44, true), new ChildNumber(60, true),
                    ChildNumber.ZERO_HARDENED, ChildNumber.ZERO);



    /**
     * 加载UI控件
     */
    private void initUi(){
        mWalletAddressText = findViewById(R.id.address);
        mWalletBalanceText = findViewById(R.id.balance);
        mToAddressEdit = findViewById(R.id.to_address);
        mAmountEdit = findViewById(R.id.amount);
        mKeyStoreEdit = findViewById(R.id.key_store);
        mPrivateKeyEdit = findViewById(R.id.private_key);
        mNetworkTitleText = findViewById(R.id.network_title);
        mToAddressBalanceText = findViewById(R.id.to_address_balance);
    }

    /**
     * 加载钱包数据
     */
    private void initEthWalletData(){
        EthWalletController.getInstance().loadWallet(this, new EthWalletController.OnWalletLoadedListener() {
            @Override
            public void onWalletLoaded(WalletFile w) {
                mWalletFile = w;
                Log.d(TAG, "onWalletLoaded::::: " + mWalletFile.getAddress().length());
                mAddress = Constants.HEX_PREFIX + mWalletFile.getAddress();
                Log.d(TAG, "mAddress当前钱包地址 ::::: " +mAddress);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        mWalletAddressText.setText(mAddress);
                        updateBalance(mAddress, mWalletBalanceText);

                        mToAddressEdit.setText(Constants.LIA_ADDRESS);
                        updateBalance(mToAddressEdit.getText().toString(),mToAddressBalanceText);
                    }
                });
            }
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //切换网络
        getMenuInflater().inflate(R.menu.eth_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.switch_goerli:
                //切换Goerli测试网络
                refurbishUI("Goerli测试网络",Constants.ETHEREUM_GOERLI_URL);
                break;
            case R.id.switch_mainnet:
                //切换Mainnet测试网络
                refurbishUI("Mainnet测试网络",Constants.ETHEREUM_MAINNET_URL);
                break;
            case R.id.switch_sepolia:
                //切换sepolia测试网络
                refurbishUI("Sepolia测试网络",Constants.ETHEREUM_SEPOLIA_URL);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * 刷新UI
     *  @param title 标题
     *  @param ethUrl 切换的网络链接
     */
    private void refurbishUI(String title,String ethUrl){
        mNetworkTitleText.setText(title);
        mWeb3j = Web3j.build(new HttpService(ethUrl));

        if (TextUtils.isEmpty(mToAddressEdit.getText())) {
            mToAddressEdit.setText("");
            mToAddressBalanceText.setText("");
        }else {
            updateBalance(mToAddressEdit.getText().toString(),mToAddressBalanceText);
        }
        if (TextUtils.isEmpty(mAddress) || TextUtils.isEmpty(mWalletAddressText.getText())) {
            mWalletAddressText.setText("");
            mWalletBalanceText.setText("");
            return;
        }else {
            updateBalance(mAddress, mWalletBalanceText);
        }
    }

    /**
     * 更新钱包余额
     *  @param owner 查询的钱包地址
     *  @param view  查询的钱包余额TextView
     */
    private void updateBalance(String owner,TextView view) {

        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.d(TAG, "updateBalance-当前查询地址：：：" + owner);
                    final BigInteger balance = mWeb3j.ethGetBalance(owner, DefaultBlockParameterName.LATEST).send().getBalance();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            BigDecimal bigDecimal = Convert.fromWei(balance.toString(), Convert.Unit.ETHER);
                            String balanceString = bigDecimal.setScale(8, RoundingMode.FLOOR).toPlainString() + " eth";
                            Log.d(TAG, "updateBalance-当前查询地址的余额：：：" + balanceString);
                            view.setText(balanceString);
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "updateBalance-查询该地址的余额失败,IOException::::"+e.getMessage().toString());
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(TAG, "updateBalance-查询该地址的余额失败,Exception::::"+e.getMessage().toString());
                }
            }
        });
    }


    /**
     * 展示钱包信息
     */
    public void showKeyStore(View view) {
        if (mWalletFile == null) {
            return;
        }
        if(TextUtils.isEmpty(mKeyStoreEdit.getText().toString().trim())){
            mKeyStoreEdit.setText(EthWalletController.getInstance().exportKeyStore(mWalletFile));
        }else {
            mKeyStoreEdit.setText("");
        }

    }


    /**
     * 展示钱包私钥
     */
    public void showPrivateKey(View view) {
        if (mWalletFile == null) {
            return;
        }
        if(TextUtils.isEmpty(mPrivateKeyEdit.getText().toString().trim())){
            mPrivateKeyEdit.setText(EthWalletController.getInstance().exportPrivateKey(mWalletFile));
        }else {
            mPrivateKeyEdit.setText("");
        }
    }

    /**
     * 更新当前钱包余额
     */
    public void updateAddressBalance(View view) {
        if (TextUtils.isEmpty(mAddress)
                || TextUtils.isEmpty(mWalletAddressText.getText())) {
            Toast.makeText(this,"当前钱包为空！",Toast.LENGTH_SHORT).show();
            return;
        }
        updateBalance(mAddress, mWalletBalanceText);
    }

    /**
     * 更新目标钱包余额
     */
    public void updateToAddressBalance(View view) {
        if (TextUtils.isEmpty(mToAddressEdit.getText())) {
            Toast.makeText(this,"目标钱包地址不能为空！",Toast.LENGTH_SHORT).show();
            return;
        }
        updateBalance(mToAddressEdit.getText().toString(),mToAddressBalanceText);
    }


    /**
     * 发送ETH
     */
    public void onSendEth(View view) {
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                if (mAddress == null || TextUtils.isEmpty(mToAddressEdit.getText().toString())
                        || TextUtils.isEmpty(mAmountEdit.getText().toString())) return;
                try {
                    BigInteger transactionCount = mWeb3j.ethGetTransactionCount(mAddress, DefaultBlockParameterName.LATEST).send().getTransactionCount();
                    BigInteger gasPrice = mWeb3j.ethGasPrice().send().getGasPrice();
                    Log.d(TAG, "run: onSendEth:::: " + transactionCount + ", " + gasPrice);
                    BigInteger gasLimit = new BigInteger("200000");
                    BigDecimal value = Convert.toWei(mAmountEdit.getText().toString().trim(), Convert.Unit.ETHER);
                    Log.d(TAG, "run: onSendEth:::: value wei" + value.toPlainString());
                    String to = mToAddressEdit.getText().toString().trim();
                    RawTransaction etherTransaction = RawTransaction.createEtherTransaction(transactionCount, gasPrice, gasLimit, to, value.toBigInteger());
                    //获取私钥，进行签名
                    ECKeyPair ecKeyPair = Wallet.decrypt("a12345678", mWalletFile);
                    Credentials credentials = Credentials.create(ecKeyPair);

                    byte[] bytes = TransactionEncoder.signMessage(etherTransaction, credentials);
                    String hexValue = Numeric.toHexString(bytes);
                    final String transactionHash = mWeb3j.ethSendRawTransaction(hexValue).send().getTransactionHash();
                    Log.d(TAG, "run: onSendEth:::: transactionHash :::" + transactionHash);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(EthWalletActivity.this, "Send success!", Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "run: onSendEth.IOException：：： " + e.getMessage());
                } catch (CipherException e) {
                    e.printStackTrace();
                    Log.d(TAG, "run: onSendEth.CipherException：：： " + e.getMessage());
                }
            }
        });
    }

}
